Add support for both prepending and appending custom menu items.
authorEmmanuele Bassi <ebassi@gnome.org>
Fri, 9 Feb 2007 14:24:19 +0000 (14:24 +0000)
committerEmmanuele Bassi <ebassi@src.gnome.org>
Fri, 9 Feb 2007 14:24:19 +0000 (14:24 +0000)
2007-02-09  Emmanuele Bassi  <ebassi@gnome.org>

* gtk/gtkrecentchoosermenu.c: Add support for both prepending
and appending custom menu items.

(gtk_recent_chooser_menu_constructor): Add a placeholder menu
item for the empty menu case, and for giving us a starting
point for the recent items populating process.

(gtk_recent_chooser_menu_insert_item),
(gtk_recent_chooser_menu_dispose_items): Insert an item at
the position following the placeholder (and find that position
if needed).

(idle_populate_func), (idle_populate_clean_up): Show the
placeholder menu item, instead of creating one each time.

(gtk_recent_chooser_menu_populate): Kill some indirections
and hide the placeholder before populating the menu.

(set_recent_manager): Remember to remove the idle population
source if the manager changes.

* tests/testrecentchoosermenu.c: Test the appending and
prepending of the menu items to the recent chooser menu
widget.

svn path=/trunk/; revision=17281

ChangeLog
gtk/gtkrecentchoosermenu.c
tests/testrecentchoosermenu.c

index 3f050e5bcdb519e39b5273034ceae7bd8f2f7788..5721c978e59d16a8a91c6fe195ee533dafe5aff0 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,30 @@
+2007-02-09  Emmanuele Bassi  <ebassi@gnome.org>
+
+       * gtk/gtkrecentchoosermenu.c: Add support for both prepending
+       and appending custom menu items.
+
+       (gtk_recent_chooser_menu_constructor): Add a placeholder menu
+       item for the empty menu case, and for giving us a starting
+       point for the recent items populating process.
+
+       (gtk_recent_chooser_menu_insert_item),
+       (gtk_recent_chooser_menu_dispose_items): Insert an item at
+       the position following the placeholder (and find that position
+       if needed).
+
+       (idle_populate_func), (idle_populate_clean_up): Show the
+       placeholder menu item, instead of creating one each time.
+
+       (gtk_recent_chooser_menu_populate): Kill some indirections
+       and hide the placeholder before populating the menu.
+
+       (set_recent_manager): Remember to remove the idle population
+       source if the manager changes.
+
+       * tests/testrecentchoosermenu.c: Test the appending and
+       prepending of the menu items to the recent chooser menu
+       widget.
+
 2007-02-08  Emmanuele Bassi  <ebassi@gnome.org>
 
        * gtk/gtkrecentchoosermenu.c (idle_populate_func): Keep count
index 7ccfb05efeb39399051d3eb6cd3aea3eb504028e..f11541a78270c47b35f5182ae3af6213b11e47df 100644 (file)
@@ -59,6 +59,9 @@ struct _GtkRecentChooserMenuPrivate
   /* max size of the menu item label */
   gint label_width;
 
+  gint first_recent_item_pos;
+  GtkWidget *placeholder;
+
   /* RecentChooser properties */
   gint limit;  
   guint show_private : 1;
@@ -237,6 +240,9 @@ gtk_recent_chooser_menu_init (GtkRecentChooserMenu *menu)
   
   priv->label_width = DEFAULT_LABEL_WIDTH;
   
+  priv->first_recent_item_pos = -1;
+  priv->placeholder = NULL;
+
   priv->current_filter = NULL;
     
   priv->tooltips = gtk_tooltips_new ();
@@ -300,19 +306,43 @@ gtk_recent_chooser_menu_dispose (GObject *object)
 
 static GObject *
 gtk_recent_chooser_menu_constructor (GType                  type,
-                                    guint                  n_construct_properties,
-                                    GObjectConstructParam *construct_params)
+                                    guint                  n_params,
+                                    GObjectConstructParam *params)
 {
   GtkRecentChooserMenu *menu;
+  GtkRecentChooserMenuPrivate *priv;
+  GObjectClass *parent_class;
   GObject *object;
   
-  object = G_OBJECT_CLASS (gtk_recent_chooser_menu_parent_class)->constructor (type,
-                                                                              n_construct_properties,
-                                                                              construct_params);
+  parent_class = G_OBJECT_CLASS (gtk_recent_chooser_menu_parent_class);
+  object = parent_class->constructor (type, n_params, params);
   menu = GTK_RECENT_CHOOSER_MENU (object);
+  priv = menu->priv;
   
-  g_assert (menu->priv->manager);
-  
+  g_assert (priv->manager);
+
+  /* we create a placeholder menuitem, to be used in the case
+   * were the menu is empty. this placeholder will stay around
+   * for the entire lifetime of the menu, and we just hide it
+   * when it's not used. we have to do this, and do it here,
+   * because we need a marker for the beginning of the recent
+   * items list, so that we can insert the new items at the
+   * right place when populating the menu, in case the user
+   * appended or prepended custom menuitems to the recent
+   * chooser menu widget.
+   */
+  priv->placeholder = gtk_menu_item_new_with_label (_("No items found"));
+  gtk_widget_set_sensitive (priv->placeholder, FALSE);
+  g_object_set_data (G_OBJECT (priv->placeholder),
+                     "gtk-recent-menu-placeholder",
+                     GINT_TO_POINTER (TRUE));
+
+  gtk_menu_shell_insert (GTK_MENU_SHELL (menu), priv->placeholder, 0);
+  gtk_widget_show (priv->placeholder);
+
+  /* (re)populate the menu */
+  gtk_recent_chooser_menu_populate (menu);
+
   return object;
 }
 
@@ -962,6 +992,41 @@ gtk_recent_chooser_menu_create_item (GtkRecentChooserMenu *menu,
   return item;
 }
 
+static void
+gtk_recent_chooser_menu_insert_item (GtkRecentChooserMenu *menu,
+                                     GtkWidget            *menuitem,
+                                     gint                  position)
+{
+  GtkRecentChooserMenuPrivate *priv = menu->priv;
+  gint real_position;
+  GList *children, *l;
+
+  if (priv->first_recent_item_pos == -1)
+    {
+      children = gtk_container_get_children (GTK_CONTAINER (menu));
+      for (real_position = 0, l = children;
+           l != NULL;
+           real_position += 1, l = l->next)
+        {
+          GtkWidget *child = l->data;
+          gint mark = 0;
+
+          mark = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (child),
+                                                     "gtk-recent-menu-placeholder"));
+          if (mark == 1)
+            break;
+        }
+      g_list_free (children);
+      priv->first_recent_item_pos = real_position;
+    }
+  else
+    real_position = priv->first_recent_item_pos;
+
+  gtk_menu_shell_insert (GTK_MENU_SHELL (menu), menuitem,
+                         real_position + position);
+  gtk_widget_show (menuitem);
+}
+
 /* removes the items we own from the menu */
 static void
 gtk_recent_chooser_menu_dispose_items (GtkRecentChooserMenu *menu)
@@ -992,6 +1057,9 @@ gtk_recent_chooser_menu_dispose_items (GtkRecentChooserMenu *menu)
         }
     }
 
+  /* recalculate the position of the first recent item */
+  menu->priv->first_recent_item_pos = -1;
+
   g_list_free (children);
 }
 
@@ -1023,29 +1091,15 @@ idle_populate_func (gpointer data)
       pdata->items = gtk_recent_chooser_get_items (GTK_RECENT_CHOOSER (pdata->menu));
       if (!pdata->items)
         {
-          item = gtk_menu_item_new_with_label (_("No items found"));
-          gtk_widget_set_sensitive (item, FALSE);
-      
-          /* we also mark this item, so that it gets removed when rebuilding
-           * the menu on the next map event
-           */
-          g_object_set_data (G_OBJECT (item), "gtk-recent-menu-mark",
-                            GINT_TO_POINTER (1));
-      
-          gtk_menu_shell_prepend (GTK_MENU_SHELL (pdata->menu), item);
-          gtk_widget_show (item);
-
+          /* show the placeholder here */
+          gtk_widget_show (priv->placeholder);
           pdata->displayed_items = 1;
 
-         /* no items: add a placeholder menu */
           GDK_THREADS_LEAVE ();
 
          return FALSE;
        }
       
-      /* reverse the list */
-      pdata->items = g_list_reverse (pdata->items);
-      
       pdata->n_items = g_list_length (pdata->items);
       pdata->loaded_items = 0;
     }
@@ -1085,16 +1139,9 @@ idle_populate_func (gpointer data)
     goto check_and_return;
       
   gtk_recent_chooser_menu_add_tip (pdata->menu, info, item);
-      
-  /* FIXME
-   *
-   * We should really place our items taking into account user
-   * defined menu items; this would also remove the need of
-   * reverting the scan order.
-   */
-  gtk_menu_shell_prepend (GTK_MENU_SHELL (pdata->menu), item);
-  gtk_widget_show (item);
-
+  gtk_recent_chooser_menu_insert_item (pdata->menu, item,
+                                       pdata->displayed_items);
+  
   pdata->displayed_items += 1;
       
   /* mark the menu item as one of our own */
@@ -1129,23 +1176,13 @@ static void
 idle_populate_clean_up (gpointer data)
 {
   MenuPopulateData *pdata = data;
+  GtkRecentChooserMenuPrivate *priv = pdata->menu->priv;
 
+  /* show the placeholder in case no item survived
+   * the filtering process in the idle loop
+   */
   if (!pdata->displayed_items)
-    {
-      GtkWidget *item;
-
-      item = gtk_menu_item_new_with_label (_("No items found"));
-      gtk_widget_set_sensitive (item, FALSE);
-
-      /* we also mark this item, so that it gets removed when rebuilding
-       * the menu on the next map event
-       */
-      g_object_set_data (G_OBJECT (item), "gtk-recent-menu-mark",
-                         GINT_TO_POINTER (1));
-      
-      gtk_menu_shell_prepend (GTK_MENU_SHELL (pdata->menu), item);
-      gtk_widget_show (item);
-    }
+    gtk_widget_show (priv->placeholder);
 
   g_slice_free (MenuPopulateData, data);
 }
@@ -1154,6 +1191,7 @@ static void
 gtk_recent_chooser_menu_populate (GtkRecentChooserMenu *menu)
 {
   MenuPopulateData *pdata;
+  GtkRecentChooserMenuPrivate *priv = menu->priv;
 
   if (menu->priv->populate_id)
     return;
@@ -1165,15 +1203,16 @@ gtk_recent_chooser_menu_populate (GtkRecentChooserMenu *menu)
   pdata->displayed_items = 0;
   pdata->menu = menu;
 
-  menu->priv->icon_size = get_icon_size_for_widget (GTK_WIDGET (menu));
+  priv->icon_size = get_icon_size_for_widget (GTK_WIDGET (menu));
   
-  /* dispose our menu items first */
+  /* remove our menu items first and hide the placeholder */
   gtk_recent_chooser_menu_dispose_items (menu);
+  gtk_widget_hide (priv->placeholder);
   
-  menu->priv->populate_id = gdk_threads_add_idle_full (G_PRIORITY_HIGH_IDLE + 30,
-                                            idle_populate_func,
-                                            pdata,
-                                            idle_populate_clean_up);
+  priv->populate_id = gdk_threads_add_idle_full (G_PRIORITY_HIGH_IDLE + 30,
+                                                idle_populate_func,
+                                                pdata,
+                                                 idle_populate_clean_up);
 }
 
 /* bounce activate signal from the recent menu item widget 
@@ -1207,7 +1246,16 @@ set_recent_manager (GtkRecentChooserMenu *menu,
   if (priv->manager)
     {
       if (priv->manager_changed_id)
-        g_signal_handler_disconnect (priv->manager, priv->manager_changed_id);
+        {
+          g_signal_handler_disconnect (priv->manager, priv->manager_changed_id);
+          priv->manager_changed_id = 0;
+        }
+
+      if (priv->populate_id)
+        {
+          g_source_remove (priv->populate_id);
+          priv->populate_id = 0;
+        }
 
       priv->manager = NULL;
     }
@@ -1221,8 +1269,6 @@ set_recent_manager (GtkRecentChooserMenu *menu,
     priv->manager_changed_id = g_signal_connect (priv->manager, "changed",
                                                  G_CALLBACK (manager_changed_cb),
                                                  menu);
-  /* (re)populate the menu */
-  gtk_recent_chooser_menu_populate (menu);
 }
 
 static gint
index 765d4e5964b9a9fc321913e837e1fec4a4f15c61..18bfb08b56a0c31b82c59bfeb47ce6bd473cd0ce 100644 (file)
@@ -56,6 +56,8 @@ static GtkWidget *
 create_recent_chooser_menu (void)
 {
   GtkWidget *menu;
+  GtkRecentFilter *filter;
+  GtkWidget *menuitem;
   
   menu = gtk_recent_chooser_menu_new_for_manager (manager);
 
@@ -66,12 +68,38 @@ create_recent_chooser_menu (void)
   gtk_recent_chooser_menu_set_show_numbers (GTK_RECENT_CHOOSER_MENU (menu),
                                             TRUE);
 
+  filter = gtk_recent_filter_new ();
+  gtk_recent_filter_set_name (filter, "Gedit files");
+  gtk_recent_filter_add_application (filter, "gedit");
+  gtk_recent_chooser_add_filter (GTK_RECENT_CHOOSER (menu), filter);
+  gtk_recent_chooser_set_filter (GTK_RECENT_CHOOSER (menu), filter);
+
   g_signal_connect (menu, "item-activated",
                     G_CALLBACK (item_activated_cb),
                     NULL);
 
   gtk_widget_show (menu);
 
+  menuitem = gtk_separator_menu_item_new ();
+  gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menuitem);
+  gtk_widget_show (menuitem);
+
+  menuitem = gtk_menu_item_new_with_label ("Test prepend");
+  gtk_menu_shell_prepend (GTK_MENU_SHELL (menu), menuitem);
+  gtk_widget_show (menuitem);
+
+  menuitem = gtk_separator_menu_item_new ();
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+  gtk_widget_show (menuitem);
+
+  menuitem = gtk_menu_item_new_with_label ("Test append");
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+  gtk_widget_show (menuitem);
+
+  menuitem = gtk_image_menu_item_new_from_stock (GTK_STOCK_CLEAR, NULL);
+  gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuitem);
+  gtk_widget_show (menuitem);
+
   return menu;
 }